package com.econ.powercloud.ems.observer;

import com.econ.powercloud.clickhouse.entity.EmsWarnRecord;
import com.econ.powercloud.common.base.facade.util.JacksonUtil;
import com.econ.powercloud.ems.common.enums.WarnType;
import com.econ.powercloud.ems.common.interf.WarnDataObserver;
import com.econ.powercloud.ems.common.pojo.EmsWarnConfigDTO;
import com.econ.powercloud.ems.common.pojo.EmsWarnStatDTO;
import com.econ.powercloud.ems.common.pojo.ProcessedDataDTO;
import com.econ.powercloud.ems.config.EMSStaticMap;
import com.econ.powercloud.ems.constant.SysConstant;
import com.econ.powercloud.ems.dal.entity.EmsDevice;
import com.econ.powercloud.ems.dal.entity.EmsWarnStatistical;
import com.econ.powercloud.ems.service.dao.EmsWarnStatisticalDAO;
import com.econ.powercloud.ems.service.dao.clickhouse.EmsWarnRecordDAO;
import com.econ.powercloud.ems.util.WarnCacheKeyUtils;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;

/**
 * @Author : zjf
 * @Description : 诊断预警处理类
 * 当发生预警时，redis中会缓存对应的记录;当预警恢复时,清除redis缓存中的数据
 * 原则上认为redis中没有该点号记录，就认为该点号处于正常的状态
 * @Date : 2022/12/5 10:59
 * @Modified by : zjf
 */
@Component
public class WarnDataDealProcessor implements WarnDataObserver {
    public static SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private EmsWarnRecordDAO emsWarnRecordDAO;
    @Autowired
    private EmsWarnStatisticalDAO emsWarnStatisticalDAO;

    @Override
    public void doInvoke(ProcessedDataDTO data) {
        Double val = Double.parseDouble(data.getVal());
        String monthTablePrefix = sdf.format(new Date(data.getTime()));
        //判断该点号是否需要处理
        EmsDevice emsDevice= EMSStaticMap.moduleCodeToDeviceMap.get(data.getDeviceCode()  + SysConstant.SPOT + (data.getModuleCode()==null?"def":data.getModuleCode()));
        if(emsDevice == null){
            return;
        }
        //先判断该点号是不是诊断预警中涉及的点号
        if(!EMSStaticMap.emsWarnConfigPid.contains(data.getPid())){
            return;
        }
        EmsWarnConfigDTO configDTO = this.getRedisWarnConfig(emsDevice,data);
        if(configDTO == null){
            return;
        }
        EmsWarnRecord emsWarnRecord = this.getRedisWarnRecord(data);
        //如果没有记录,即上一次该点号的记录没有达到预警的要求,只需判断现在的实时值
        if(emsWarnRecord == null){
            EmsWarnRecord warnRecord = null;
            if(configDTO.getWarnUpper() != null && configDTO.getWarnUpper() < val){
                //生成一条超过预警上限的预警记录
                warnRecord = this.getEmsWarnRecord(data,WarnType.TOO_HIGH,configDTO.getPidName());
            }else if(configDTO.getWarnLower() != null && configDTO.getWarnLower() > val){
                //生成一条低于预警下限的预警记录
                warnRecord = this.getEmsWarnRecord(data,WarnType.TOO_LOW,configDTO.getPidName());
            }
            this.dealWithWarnRecord(emsDevice.getDeviceCode(), warnRecord,monthTablePrefix,data);
            return;
        }
        monthTablePrefix = sdf.format(new Date(emsWarnRecord.getWarnTime()));
        //如果有过高的记录
        if(Objects.equals(emsWarnRecord.getType(), WarnType.TOO_HIGH.getCode())){
            if(configDTO.getWarnUpper() < val){
                return;
            }
            emsWarnRecord.setRecoveryTime(data.getTime());
            //更新操作记录
            emsWarnRecordDAO.updateById(monthTablePrefix,emsWarnRecord);
            EmsWarnRecord warnRecord = null;
            if(configDTO.getWarnLower() != null && configDTO.getWarnLower() > val){
                //生成一条低于预警下限的预警记录
                warnRecord = this.getEmsWarnRecord(data,WarnType.TOO_LOW,configDTO.getPidName());
            }
            this.dealWithWarnRecord(emsDevice.getDeviceCode(), warnRecord,monthTablePrefix,data);
            return;
        }
        //如果是过低的记录
        if(Objects.equals(emsWarnRecord.getType(), WarnType.TOO_LOW.getCode())){
            if(configDTO.getWarnLower() > val){
                return;
            }
            emsWarnRecord.setRecoveryTime(data.getTime());
            //更新操作记录
            emsWarnRecordDAO.updateById(monthTablePrefix,emsWarnRecord);
            EmsWarnRecord warnRecord = null;
            if(configDTO.getWarnUpper() != null && configDTO.getWarnUpper() < val){
                //生成一条高于预警上限的预警记录
                warnRecord = this.getEmsWarnRecord(data,WarnType.TOO_HIGH,configDTO.getPidName());
            }
            this.dealWithWarnRecord(emsDevice.getDeviceCode(), warnRecord,monthTablePrefix,data);
            return;
        }
    }

    /**
     * 获取诊断预警点号配置信息
     * @param emsDevice
     * @param data
     * @return
     */
    private EmsWarnConfigDTO getRedisWarnConfig(EmsDevice emsDevice,ProcessedDataDTO data){
        String configHashKey = WarnCacheKeyUtils.getWarnConfigKey(emsDevice.getDeviceCode());
        //获取该点号的预警配置, hashKey: pid + "-" + ditType
        String configRedisVal = (String)stringRedisTemplate.opsForHash().get(configHashKey, String.format("%s-%s", data.getPid(), data.getDitType()));
        if(configRedisVal == null){
            return null;
        }
        return JacksonUtil.parseObject(configRedisVal,EmsWarnConfigDTO.class);
    }

    /**
     * 获取诊断预警点号记录
     * @param data
     * @return
     */
    private EmsWarnRecord getRedisWarnRecord(ProcessedDataDTO data){
        String recordHashKey = WarnCacheKeyUtils.getWarnRecordKey();
        //获取该点号的预警记录, hashKey: pid + "-" + ditType
        String recordRedisVal = (String)stringRedisTemplate.opsForHash().get(recordHashKey,String.format("%s-%s", data.getPid(), data.getDitType()));
        if(recordRedisVal == null){
            return null;
        }
        return JacksonUtil.parseObject(recordRedisVal,EmsWarnRecord.class);
    }

    private EmsWarnRecord getEmsWarnRecord(ProcessedDataDTO data,WarnType warnType,String pidName){
        return EmsWarnRecord.builder()
                .id(data.getPid() + SysConstant.SPOT + data.getTime())
                .pid(data.getPid()).content(String.format("%s%s",pidName,warnType.getDesc()))
                .warnTime(data.getTime()).type(warnType.getCode()).val(data.getVal())
                .recoveryTime(data.getTime())
                .build();
    }

    private void dealWithWarnRecord(String deviceCode, EmsWarnRecord warnRecord, String monthTablePrefix, ProcessedDataDTO data){
        String recordHashKey = WarnCacheKeyUtils.getWarnRecordKey();
        if(warnRecord != null){
            emsWarnRecordDAO.insertBatch(monthTablePrefix,Lists.newArrayList(warnRecord));
            //redis缓存
            stringRedisTemplate.opsForHash().put(recordHashKey,String.format("%s-%s", data.getPid(), data.getDitType()),JacksonUtil.toJSONString(warnRecord));
            updateMysqlAndRedisWarnStat(deviceCode, data.getPid());
        }else{
            //删除redis缓存
            stringRedisTemplate.opsForHash().delete(recordHashKey,String.format("%s-%s", data.getPid(), data.getDitType()));
        }
    }

    /**
     * 更新redis中预警的统计数据
     * @param deviceCode ems设备编号
     * @param pid 点号
     */
    private void updateMysqlAndRedisWarnStat(String deviceCode, String pid){
        String jsonValue =  (String)stringRedisTemplate.opsForHash().get(WarnCacheKeyUtils.getWarnStatKey(),deviceCode);
        if(jsonValue == null){
            return;
        }
        EmsWarnStatDTO emsWarnStatDTO = JacksonUtil.parseObject(jsonValue, EmsWarnStatDTO.class);
        emsWarnStatDTO.setWarnTotalNum(emsWarnStatDTO.getWarnTotalNum() + 1);
        emsWarnStatDTO.setWarnTodayNum(emsWarnStatDTO.getWarnTodayNum() + 1);
        emsWarnStatDTO.getPids().add(pid);
        stringRedisTemplate.opsForHash().put(WarnCacheKeyUtils.getWarnStatKey(),deviceCode, JacksonUtil.toJSONString(emsWarnStatDTO));
        //更新mysql操作
        EmsWarnStatistical emsWarnStatistical = EmsWarnStatistical.builder()
                .id(emsWarnStatDTO.getId()).warnTodayNum(emsWarnStatDTO.getWarnTodayNum())
                .warnTotalNum(emsWarnStatDTO.getWarnTotalNum())
                .warnTodayPidNum(CollectionUtils.isEmpty(emsWarnStatDTO.getPids())?0:emsWarnStatDTO.getPids().size())
                .build();
        emsWarnStatisticalDAO.updateById(emsWarnStatistical);
    }

}
